#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import json
import traceback
from urllib import request
from urllib import error
import os
import hashlib
import sys
import socket
import time


def removeDir(directory):
    if os.path.isdir(directory):
        directory = directory.replace("\\", "/")
        if directory[len(directory)-1] != '/':
            directory = directory + "/"

    if os.path.isdir(directory):
        for p in os.listdir(directory):
            removeDir(directory + p)
        if os.path.exists(directory):
            os.rmdir(directory)
    else:
        if os.path.exists(directory):
            os.remove(directory)


class DownloadParser(object):
    def __init__(self, srcProjectFile, dstProjectFile):
        if not os.path.exists(srcProjectFile):
            print("src/project.manifest not exist")
            self.bCfgError = True
            return

        if not os.path.exists(dstProjectFile):
            self.bCfgError = True
            print("dst/project.manifest not exist")
            return

        try:
            self.srcJson = DownloadParser.readFile(srcProjectFile)
        except Exception as e1:
            traceback.print_exc()
            input("pause")

        try:
            self.dstJson = DownloadParser.readFile(dstProjectFile)
        except Exception as e1:
            traceback.print_exc()
            input("pause")

        DownloadParser.error = False
        self.bCfgError = False
        self.failFiles = []
        # print(self.srcJson['remoteVersionUrl'])

    def download(self):
        if self.bCfgError:
            return

        socket.setdefaulttimeout(20)
        # print(os.path.abspath(__file__))
        print("---------------game start--------------")
        sys.setrecursionlimit(90000)
        removeDir("res")
        os.makedirs("res")

        assets = self.srcJson['assets']
        srcKeys = assets.keys()

        dstAssets = self.dstJson['assets']
        dstKeys = dstAssets.keys()
        downloadPath = self.dstJson['packageUrl']

        for key in dstKeys:
            # print(downloadPath + key)
            dstItem = dstAssets[key]
            tempItem = None
            tempItem = assets.get(key, None)
            if tempItem and tempItem['md5'] == dstItem['md5']:
                continue
            self.downloadItem(downloadPath, key, dstAssets)

        retryTimes = 5
        while len(self.failFiles) > 0 and retryTimes >= 0:
            retryTimes -= 1
            self.downloadFailFile(downloadPath, dstAssets, retryTimes)

        if DownloadParser.error:
            print("-------------download occurs error,please check res/err.log---------------")
        else:
            print("--------------------game over--------------------")

    def downloadFailFile(self, dp, assets, retry):
        temp = self.failFiles
        self.failFiles = []
        for k in temp:
            self.downloadItem(dp, k, assets, retry=retry)

    def downloadItem(self, dp, k, assets, times=1, timeout=100, retry=5):
        # time.sleep(1)
        req = request.Request(dp + k)

        # noinspection PyBroadException
        try:
            response = request.urlopen(req, timeout=timeout)
            freak = response.read()
            dstMd5 = hashlib.md5(freak).hexdigest()
            response.close()
            if dstMd5 != assets[k]['md5']:
                if times == 1:
                    times = 5
                else:
                    times -= 1

                if times <= 1:
                    # print("下载文件有问题", k)
                    DownloadParser.save(k + ' md5 verify failed\n', "res/err.log", "a")
                    DownloadParser.error = True
                else:
                    # print("retry filename = ", k)
                    self.downloadItem(dp, k, assets, times)
            else:
                print("filename = ", k)
                DownloadParser.save(freak, "res/" + k)
        except error.URLError as e2:
            if hasattr(e2, "code"):
                DownloadParser.error = True
                errString = "{} {} {}".format(k, e2.code, '\n')
                DownloadParser.save(errString, "res/err.log", "a")
            elif hasattr(e2, "reason"):
                DownloadParser.error = True
                # print("reason = ", e.reason)
                errString = "{} {} {}".format(k, e2.reason, '\n')
                DownloadParser.save(errString, "res/err.log", "a")
        except ConnectionResetError as cone:
            # print("ConnectionResetError")
            # print(cone)
            self.doRetry(retry, k)
        except TimeoutError as toe:
            print("TimeoutError")
            print(toe)
            self.doRetry(retry, k)
        except Exception as ee:
            DownloadParser.error = True
            errString = "{} {}".format(k, '\n')
            DownloadParser.save(errString, "res/err.log", "a")
            f = open("res/err.log", 'a')
            traceback.print_exc(file=f)
            f.write("\n")
            f.flush()
            f.close()

    def doRetry(self, retry, key):
        if retry == 0:
            DownloadParser.error = True
            errString = "{} {}".format(key, '\n')
            DownloadParser.save(errString, "res/err.log", "a")
            f = open("res/err.log", 'a')
            traceback.print_exc(file=f)
            f.write("\n")
            f.flush()
            f.close()
        else:
            self.failFiles.append(key)

    @staticmethod
    def save(f, path, mode="wb"):
        dir = os.path.split(path)[0]
        if not os.path.exists(dir):
            os.makedirs(dir)
        # noInspection PyBroadException
        try:
            with open(path, mode) as file:
                file.write(f)
        except Exception as e3:
            print("保存失败")

    @staticmethod
    def readFile(fileName):
        f = open(fileName, "r", encoding="utf-8")
        result = json.load(f)
        f.close()
        return result


def main():
    parser = DownloadParser("src/project.manifest", "dst/project.manifest")
    parser.download()


if __name__ == '__main__':
    # noInspection PyBroadException
    try:
        main()
    except Exception as e1:
        traceback.print_exc()
